/*LICENSE_BEGIN
 * ******************************************************************
 * Copyright 2006 by the Dainamite Robocup-Team at the
 * DAI-Labor, TU-Berlin (www.dai-labor.de)
 * 
 * Members of Dainamite are:
 * Holger Endert
 * Robert Wetzker
 * Thomas Karbe
 * Axel Heßler
 * Philippe Bütter
 * Felix Brossmann
 * 
 * Licensed under the GNU GPL (General Public License) Free Software 
 * License, Version 2. You may use this file only in compliance with 
 * the License. You may obtain a copy of the License at:
 * 
 * http://www.fsf.org/licensing/licenses/gpl.txt
 * ******************************************************************
LICENSE_END*/
package robocup.component.coach;

import java.util.LinkedList;

import robocup.component.ControllerNumbers.PLAY_MODE;
import robocup.component.infotypes.AuralInfo;
import robocup.component.infotypes.CoachSayInfo;
import robocup.component.infotypes.CoachVisualInfo;
import robocup.component.infotypes.PlayerTypesInfo;
import robocup.component.speechacts.coachlanguage.TypeConstants;


/**
 * The <code>BroadcastCommand</code> is responsible for determining the optimal
 * sending time for certain messages. These are different for freeform and other
 * clang messages (see <code>SConf.CLANG_WIN_SIZE</code> and <code>
 * SConf.FREEFORM_WAIT_PERIOD</code>). Therefore it collects all messages in
 * different lists, and sends them together, if a new sending interval is 
 * reached.
 */
public class BroadcastCommand implements InfoReceiver {

	/**
	 * Reference to the online coach-tactic. Needed to send messages.
	 */
	private OLCoachTactic olct = null;

	/**
	 * Reference to the world model of the coach.
	 */
	private CoachWorld cwm = null;

	/**
	 * Counter for freeform messages.
	 */
	private int freeformCount = 0;

	/**
	 * Cycle, in which the last rule was sent.
	 */
	private int lastSendRule = 0;

	/**
	 * Cycle, in which the last define message was sent.
	 */
	private int lastSendDefine = 0;

	/**
	 * Cycle, in which the last advice message was sent.
	 */
	private int lastSendAdvice = 0;

	/**
	 * Cycle, in which the last info message was sent.
	 */
	private int lastSendInfo = 0;

	/**
	 * Cycle, in which the last meta message was sent.
	 */
	private int lastSendMeta = 0;

	/**
	 * Cycle, in which the last delete (-rule) message was sent.
	 */
	private int lastSendDelete = 0;

	/**
	 * List for collecting freeform-messages.
	 */
	private LinkedList<String> freeformMessages = new LinkedList<String>();

	/**
	 * List for collecting rule-messages.
	 */
	private LinkedList<CoachSayInfo> ruleMessages = new LinkedList<CoachSayInfo>();

	/**
	 * List for collecting advice-messages.
	 */
	private LinkedList<CoachSayInfo> adviceMessages = new LinkedList<CoachSayInfo>();

	/**
	 * List for collecting info-messages.
	 */
	private LinkedList<CoachSayInfo> infoMessages = new LinkedList<CoachSayInfo>();

	/**
	 * List for collecting meta-messages.
	 */
	private LinkedList<CoachSayInfo> metaMessages = new LinkedList<CoachSayInfo>();

	/**
	 * List for collecting delete-messages.
	 */
	private LinkedList<CoachSayInfo> deleteMessages = new LinkedList<CoachSayInfo>();

	/**
	 * List for collecting define-messages.
	 */
	private LinkedList<CoachSayInfo> defineMessages = new LinkedList<CoachSayInfo>();

	/**
	 * Constructor of an action that is resonsible for sending commands to the
	 * soccer server in allowed intervals
	 * 
	 * @param olct -
	 *            the parent object, the OLCoachTactic
	 */
	public BroadcastCommand(OLCoachTactic olct) {
		this.olct = olct;
		this.cwm = olct.getCoachWorld();

	}


	/**
	 * Adds a new free-form message to the corresponding list. This is only
	 * possible, if the message is not yet longer than <code>
	 * SConf.SAY_COACH_MSG_SIZE</code>.
	 * 
	 * @param message - the message, which should be appended
	 */
	public void appendFreeform(String message) {
		if (this.freeformMessages.isEmpty())
			this.freeformMessages.add(message);
		else if ((this.cwm.getSConf().SAY_COACH_MSG_SIZE - this.freeformMessages
				.getLast().length()) > message.length()) {
			message = this.freeformMessages.getLast() + message;
			this.freeformMessages.removeLast();
			this.freeformMessages.add(message);
		} else
			this.freeformMessages.add(message);
	}

	/**
	 * Appends a Clang Message (CoachsayInfo as super-type) to the corresponding
	 * list (either ADVICE, DEFINE, DELETE, INFO, META or RULE).
	 * 
	 * @param csi - the message, which should be sent.
	 */
	public void appendCSI(CoachSayInfo csi) {
		TypeConstants.MESSAGETYPE type = csi.getMessageType();

		switch (type) {

		case ADVICE:
			this.adviceMessages.add(csi);
			break;

		case DEFINE:
			if (this.defineMessages.isEmpty()) {
				this.defineMessages.add(csi);
			} else {
				this.defineMessages.getLast()
						.addDefineList(csi.getDefineList());
			}
			break;

		case DELETE:
			this.deleteMessages.add(csi);
			break;

		case INFO:
			this.infoMessages.add(csi);
			break;

		case META:
			this.metaMessages.add(csi);
			break;

		case RULE:
			this.ruleMessages.add(csi);
			break;

		}

	}

	/**
	 * Checks, which previously added messages can be sent, depending on the
	 * allowed message intervals and on their existance. 
	 */
	public void sayAction() {

		if (((this.cwm.getPlayMode() != PLAY_MODE.PLAY_ON) || ((this.cwm
				.getVisualCycle() - this.lastSendDefine) >= this.cwm.getSConf().CLANG_WIN_SIZE))
				&& (!this.defineMessages.isEmpty())) {

			this.olct.sendDirect(this.defineMessages.removeFirst().toString());

			this.lastSendDefine = this.cwm.getVisualCycle();
			System.out.println(this.cwm.getVisualCycle()
					+ ". Coach is sending broadcasted define messages...");
		}

		if (((this.cwm.getPlayMode() != PLAY_MODE.PLAY_ON) || ((this.cwm
				.getVisualCycle() - this.lastSendInfo) >= this.cwm.getSConf().CLANG_WIN_SIZE))
				&& (!this.infoMessages.isEmpty())) {
			this.olct.sendDirect(this.infoMessages.removeFirst().toString());
			this.lastSendInfo = this.cwm.getVisualCycle();
			System.out.println(this.cwm.getVisualCycle()
					+ ". Coach is sending broadcasted info messages...");
		}

		if (((this.cwm.getPlayMode() != PLAY_MODE.PLAY_ON) || ((this.cwm
				.getVisualCycle() - this.lastSendMeta) >= this.cwm.getSConf().CLANG_WIN_SIZE))
				&& (!this.metaMessages.isEmpty())) {
			this.olct.sendDirect(this.metaMessages.removeFirst().toString());
			this.lastSendMeta = this.cwm.getVisualCycle();
			System.out.println(this.cwm.getVisualCycle()
					+ ". Coach is sending broadcasted meta messages...");
		}

		if (((this.cwm.getPlayMode() != PLAY_MODE.PLAY_ON) || ((this.cwm
				.getVisualCycle() - this.lastSendAdvice) >= this.cwm.getSConf().CLANG_WIN_SIZE))
				&& (!this.adviceMessages.isEmpty())) {
			this.olct.sendDirect(this.adviceMessages.removeFirst().toString());
			this.lastSendAdvice = this.cwm.getVisualCycle();
			System.out.println(this.cwm.getVisualCycle()
					+ ". Coach is sending broadcasted advice messages...");
		}

		if (((this.cwm.getPlayMode() != PLAY_MODE.PLAY_ON) || ((this.cwm
				.getVisualCycle() - this.lastSendRule) >= this.cwm.getSConf().CLANG_WIN_SIZE))
				&& (!this.ruleMessages.isEmpty())) {
			this.olct.sendDirect(this.ruleMessages.removeFirst().toString());
			this.lastSendRule = this.cwm.getVisualCycle();
			System.out.println(this.cwm.getVisualCycle()
					+ ". Coach is sending broadcasted rule messages...");
		}

		if (((this.cwm.getPlayMode() != PLAY_MODE.PLAY_ON) || ((this.cwm
				.getVisualCycle() - this.lastSendDelete) >= this.cwm.getSConf().CLANG_WIN_SIZE))
				&& (!this.deleteMessages.isEmpty())) {
			this.olct.sendDirect(this.deleteMessages.removeFirst().toString());
			this.lastSendDelete = this.cwm.getVisualCycle();
			System.out.println(this.cwm.getVisualCycle()
					+ ". Coach is sending broadcasted delete messages...");
		}

		if ((this.cwm.getPlayMode() != PLAY_MODE.PLAY_ON)
				|| (this.cwm.getPlayOnCycle() > this.cwm.getSConf().FREEFORM_WAIT_PERIOD)
				&& (this.cwm.getPlayOnCycle() < (this.cwm.getSConf().FREEFORM_WAIT_PERIOD + this.cwm
						.getSConf().FREEFORM_SEND_PERIOD))
				&& (this.freeformCount < this.cwm.getSConf().SAY_COACH_COUNT_MAX)) {

			if (!this.freeformMessages.isEmpty()) {
				String str = "(say (freeform \""
						+ this.freeformMessages.removeFirst() + "\"))";
				this.olct.sendDirect(str);
				this.freeformCount++;
			}

		}

	}

	/**
	 * Implemented from <code>InfoReceiver-Interface</code>. Not used.
	 * 
	 * @param the latest received player-type info
	 */
	public void receivedPlayerTypeInf(PlayerTypesInfo info) {
	}

	/**
	 * Implemented from <code>InfoReceiver-Interface</code>. Not used.
	 * 
	 * @param the latest received aural-info
	 */
	public void receivedAuralInfo(AuralInfo info) {
	}

	/**
	 * Implemented from <code>InfoReceiver-Interface</code>. Invokes 
	 * <code>sayAction()</code>, which checks, whether new messages can be sent.
	 * 
	 * @param the latest received coach-visual-info.
	 */
	public void receivedVisualInfo(CoachVisualInfo info) {
		this.sayAction();
	}

}
